package wangwenjun.phase1.synchronizeddemo.booleanlock;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;

import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;

/**
 * @author ChangLiang
 * @date 2020/6/10
 */
@Slf4j
public class BooleanLockTestV1 {

    private final Lock lock = new BooleanLockV1();

    public void syncMethod() {
        try {
            lock.lock();
            int randomInt = ThreadLocalRandom.current().nextInt(10);
            log.info(Thread.currentThread() + " getOutput the lock.");
            TimeUnit.SECONDS.sleep(randomInt);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void syncMethodTimeout() {
        try {
            lock.lock(2000);
            log.info(currentThread() + " getOutput the lock.");
            int randomInt = current().nextInt(10);
            TimeUnit.SECONDS.sleep(randomInt);
        } catch (InterruptedException | TimeoutException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Test
    @DisplayName("多个线程通过lock方法争抢锁")
    public void test() throws InterruptedException {
        BooleanLockTestV1 lockTest = new BooleanLockTestV1();
        IntStream.range(0, 100).mapToObj(i -> new Thread(lockTest::syncMethod, "my-thread-" + i))
                .forEach(Thread::start);
        Thread.currentThread().join();
    }


    @Test
    @DisplayName("可中断被阻塞的线程")
    public void test4() throws InterruptedException {
        BooleanLockTestV1 lockTest = new BooleanLockTestV1();
        Thread t1 = new Thread(lockTest::syncMethod, "T1");
        t1.start();
        TimeUnit.MILLISECONDS.sleep(20);

        Thread t2 = new Thread(lockTest::syncMethod, "T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);

        // t2线程在 this.wait(); 也就是说在wait set中 是可以被打断的
        // 使用synchronized 其是在entry set中 是无法被打断的
        t2.interrupt();

        log.info("blocked threads: {}", lockTest.lock.getBlockedThreads());
        t2.join();
        t1.join();
    }

    @Test
    @DisplayName("中断被阻塞的线程")
    public void test2() throws InterruptedException {
        BooleanLockTestV1 lockTest = new BooleanLockTestV1();

        Thread t1 = new Thread(()->{
            lockTest.syncMethod();
            log.info("{} getBlockedThreads size = {}", Thread.currentThread(),lockTest.lock.getBlockedThreads());
        }, "T1");
        t1.start();
        TimeUnit.MILLISECONDS.sleep(20);

        Thread t2 = new Thread(()->{
            lockTest.syncMethodTimeout();
            log.info("{} getBlockedThreads size = {}", Thread.currentThread(),lockTest.lock.getBlockedThreads());
        }, "T2");
        t2.start();

        TimeUnit.MILLISECONDS.sleep(10);

        log.info("{} getBlockedThreads size = {}", Thread.currentThread(),lockTest.lock.getBlockedThreads());

        /// not equal this用错了
        /*log.info(lockTest.toString());
        log.info(this.toString());*/

        // expect: 1  actual: 0
/*        log.info("blocked threads: {}", this.getBlockedThread());
        TimeUnit.SECONDS.sleep(2);
        log.info("blocked threads: {}", this.getBlockedThread());*/

        t2.join();
        t1.join();
    }

    @Test
    @DisplayName("中断被阻塞的线程")
    public void test3() throws InterruptedException {
        BooleanLockTestV1 lockTest = new BooleanLockTestV1();

        Thread t1 = new Thread(lockTest::syncMethod, "T1");
        t1.start();
        TimeUnit.MILLISECONDS.sleep(20);

        Thread t2 = new Thread(lockTest::syncMethodTimeout, "T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);

        // expect: 1  actual: 0
        log.info("blocked threads: {}", lockTest.lock.getBlockedThreads());
        TimeUnit.SECONDS.sleep(2);
        log.info("blocked threads: {}", lockTest.lock.getBlockedThreads());

        t2.join();
        t1.join();
    }
}
